import {
  objectToString,
  objectToStringFormat,
  singleLinkedList
} from "../../shared";
import { AbstractList, IListConstructor } from "./list-type";
import { ListNode } from "./list-node";
/**
 * 单链表构造器
 */
export interface SingleLinkedIListConstructor<T> extends IListConstructor<T> {
  new (datas: T[]): SingleLinkedList<T>;
  readonly [Symbol.species]: SingleLinkedIListConstructor<T>;
  readonly prototype: SingleLinkedList<T>;
  isSingleLinkedList<T>(obj: unknown): obj is SingleLinkedList<T>;
  of<T>(...datas: T[]): SingleLinkedList<T>;
  from<T>(datas: T[]): SingleLinkedList<T>;
}
/**
 * 判断是否是单链表
 * @param obj 任意对象
 */
export function isSingleLinkedList<T>(
  obj: unknown
): obj is SingleLinkedList<T> {
  return obj instanceof SingleLinkedList;
}
/**
 * 单链表，
 * 无头结点
 */
export class SingleLinkedList<T> extends AbstractList<T> {
  static [Symbol.hasInstance](instance: unknown): boolean {
    return objectToString(instance) === objectToStringFormat(singleLinkedList);
  }
  static from<T>(datas: T[]): SingleLinkedList<T> {
    return new SingleLinkedList(datas);
  }
  static of<T>(...datas: T[]): SingleLinkedList<T> {
    return new SingleLinkedList(datas);
  }
  static isSingleLinkedList<T>(obj: unknown): obj is SingleLinkedList<T> {
    return isSingleLinkedList(obj);
  }
  static get [Symbol.species](): SingleLinkedIListConstructor<any> {
    return SingleLinkedList;
  }
  [Symbol.toStringTag] = singleLinkedList;
  constructor(datas: T[] = []) {
    super();
    for (const el of datas) this.add(el);
  }
  add(element: T, index: number = this._size): void {
    this.checkAddRange(index);
    if (index === 0) {
      this._firstNode = new ListNode<T>(element, null, this._firstNode);
    } else {
      for (const [k, v] of this.entriesForNode())
        if (k === index - 1) {
          v.next = new ListNode<T>(element, null, v.next);
          break;
        }
    }
    this._size++;
  }
  delete(index: number): T {
    this.checkRange(index);
    let el = (this._firstNode as ListNode<T>).element;
    if (this._size === 1) {
      this.clear();
    } else {
      for (const [k, v] of this.entriesForNode())
        if (k === index - 1) {
          const next = v.next as ListNode<T>;
          el = next.element;
          v.next = next.next;
          break;
        }
      this._size--;
    }
    return el;
  }
}
